package de.recipeminer.persistence;

import com.db4o.Db4oEmbedded;
import com.db4o.ObjectContainer;
import com.db4o.ObjectSet;
import com.db4o.config.EmbeddedConfiguration;
import com.db4o.ext.DatabaseClosedException;
import com.db4o.ext.DatabaseReadOnlyException;
import com.db4o.ext.Db4oIOException;
import com.db4o.ext.ExtObjectContainer;
import com.db4o.query.Predicate;
import com.db4o.query.Query;
import com.db4o.query.QueryComparator;
import org.apache.log4j.Logger;

import java.io.File;
import java.util.Comparator;

/**
 * An adapter class for ObjectContainer, providing the same operation as the adapted
 * class, extended by addional convenience methods.
 *
 * @author Markus Ackermann
 */
public class Db4oContainer implements ObjectContainer{
    static Logger logger = Logger.getLogger(Db4oContainer.class);
    private ObjectContainer container;
    private File dataBaseFile;
    private Db4oDescriptor.EmbeddedConfigurationProvider configurationProvider;           

    protected Db4oContainer(Db4oDescriptor.EmbeddedConfigurationProvider configProvider, File dbFile) {
        dataBaseFile = dbFile;        
        configurationProvider = configProvider;
    }

    /**
     * Deletes all Objects stored in this database.
     * Handle with care!
     *
     * @return number of deleted objects
     */
    public long clear() {
        ObjectSet<Object> result = query(new Predicate<Object>() {
            @Override
            public boolean match(Object o) {
                return true;
            }
        });
        logger.info(String.format("Clearing %d objects.", result.size()));
        while (result.hasNext()) {
            delete(result.next());
        }
        commit();
        return result.size();
    }

    void open() {
        if (container == null || container.ext().isClosed()) {
            logger.info("Opening database file: " + getDataBaseFile().getAbsolutePath());
            EmbeddedConfiguration config = configurationProvider.getConfig();
            container = Db4oEmbedded.openFile(config, dataBaseFile.getPath());
        }
    }

    public void closeAllSessions() {
        open();
        if(!container.ext().isClosed()){
            logger.debug("Closing database file: " + getDataBaseFile().getAbsolutePath());
            int sessions = -1;
            while(!container.close())
                sessions++;
            if(sessions > 0)
                logger.warn(String.format("Closed %d remaining sessions.", sessions));
        }
    }


    /**
     * For testing purpose, to see if objects really are persisted,
     * not just lingering in the Object cache of a db4o session.
     */ 
    public void reopen() {
        closeAllSessions();
        open();
    }

    public File getDataBaseFile() {
        return dataBaseFile;
    }

    @Override
    public void activate(Object obj, int depth) throws Db4oIOException, DatabaseClosedException {
        open();
        container.activate(obj, depth);
    }

    @Override
    public boolean close() throws Db4oIOException {
        return container != null ?  container.close() : true;
    }

    @Override
    public void commit() throws Db4oIOException, DatabaseClosedException, DatabaseReadOnlyException {
        open();
        container.commit();
    }

    @Override
    public void deactivate(Object obj, int depth) throws DatabaseClosedException {
        open();
        container.deactivate(obj, depth);
    }

    @Override
    public void delete(Object obj) throws Db4oIOException, DatabaseClosedException, DatabaseReadOnlyException {
        open();
        container.delete(obj);
    }

    @Override
    public ExtObjectContainer ext() {
        open();
        return container.ext();
    }

    @Override
    public <T> ObjectSet<T> queryByExample(Object template) throws Db4oIOException, DatabaseClosedException {
        open();
        return container.queryByExample(template);
    }

    @Override
    public Query query() throws DatabaseClosedException {
        open();
        return container.query();
    }

    @Override
    public <TargetType> ObjectSet<TargetType> query(Class<TargetType> class_) 
        throws Db4oIOException, DatabaseClosedException {
        open();
        return container.query(class_);
    }

    @Override
    public <TargetType> ObjectSet<TargetType> query(Predicate<TargetType> predicate)
        throws Db4oIOException, DatabaseClosedException {
        open();
        return container.query(predicate);
    }

    @Override
    public <TargetType> ObjectSet<TargetType> query(Predicate<TargetType> predicate,
                                                    QueryComparator<TargetType> comparator)
            throws Db4oIOException, DatabaseClosedException {
        open();
        return container.query(predicate, comparator);
    }

    @Override
    public <TargetType> ObjectSet<TargetType> query(Predicate<TargetType> predicate, Comparator<TargetType> comparator)
        throws Db4oIOException, DatabaseClosedException {
        open();
        return container.query(predicate, comparator);
    }

    @Override
    public void rollback() throws Db4oIOException, DatabaseClosedException, DatabaseReadOnlyException {
        open();
        container.rollback();
    }

    @Override
    public void store(Object obj) throws DatabaseClosedException, DatabaseReadOnlyException {
        open();
        container.store(obj);
    }
}
